package checker

import (
	"kumachan/standalone/rpc/vd"
	"kumachan/interpreter/lang/common/source"
	"kumachan/interpreter/compiler/checker/typsys"
)

func makeSchemaTable(types ([] TypeDef), mic ModuleInfoCollection) (vd.SchemaTable, VdTypeInfoMapping, source.Errors) {
	var mapping, err = makeVdTypeInfoMapping(types, mic)
	if err != nil { return nil, nil, err }
	var table = make(vd.SchemaTable)
	{ var err = (func() source.Errors {
	var errs source.Errors
	for _, def := range types {
		var info, has_info = mapping[def.TypeDef]
		if !(has_info) { panic("something went wrong") }
		var id = info.Id
		var err = (func() *source.Error {
		var schema, err = (func() (vd.Schema, *source.Error) {
			switch content := def.Content.(type) {
			case *typsys.Box:
				var loc = def.Location
				{ var record, is_record = getRecord(content.InnerType)
				if is_record {
					var fields_vd = make(map[string] vd.RecordField)
					for i, field := range record.Fields {
						var field_t = field.Type
						var field_vd, err = vdGetType(field_t, mapping, loc)
						if err != nil { return nil, err }
						fields_vd[field.Name] = vd.RecordField {
							Type:  field_vd,
							Index: uint(i),
						}
					}
					return vd.RecordSchema { Fields: fields_vd }, nil
				} }
				{ var tuple, is_tuple = getTuple(content.InnerType)
				if is_tuple {
					var elements_vd = make([] *vd.Type, len(tuple.Elements))
					for i, el := range tuple.Elements {
						var el_vd, err = vdGetType(el, mapping, loc)
						if err != nil { return nil, err }
						elements_vd[i] = el_vd
					}
					return vd.TupleSchema { Elements: elements_vd }, nil
				} }
				var inner = content.InnerType
				var el_vd, err = vdGetType(inner, mapping, loc)
				if err != nil { return nil, err }
				return vd.TupleSchema { Elements: [] *vd.Type { el_vd } }, nil
			case *typsys.Enum:
				var indexes = make(map[vd.TypeId] uint)
				for i, case_def := range content.CaseTypes {
					var case_info, exists = mapping[case_def]
					if !(exists) {
						return nil, source.MakeError(def.Location,
							E_EnumCaseNotSerializable {
								Case: case_def.Name.ItemName,
							})
					}
					var case_id = case_info.Id
					indexes[case_id] = uint(i)
				}
				return vd.EnumSchema { CaseIndexMap: indexes }, nil
			default:
				panic("something went wrong")
			}
		})()
		if err != nil { return err }
		table[id] = schema
		return nil
		})()
		source.ErrorsJoin(&errs, err)
	}
	return errs
	})()
	if err != nil { return nil, nil, err }
	return table, mapping, nil }
}

func makeVdTypeInfoMapping(types ([] TypeDef), mic ModuleInfoCollection) (VdTypeInfoMapping, source.Errors) {
	var mapping = make(VdTypeInfoMapping)
	var occurred = make(map[vd.TypeId] struct{})
	var err = (func() source.Errors {
	var errs source.Errors
	for _, def := range types {
		var mi = mic.GetTypeModuleInfo(def)
		var manifest = &(mi.ModManifest)
		var meta = &(def.DataConfig)
		var id = vd.MakeTypeId (
			"",
			manifest.UniqueId,
			meta.Name,
			"",
		)
		var err = (func() (*source.Error) {
			// 1. checked duplicate
			var _, duplicate = occurred[id]
			if duplicate {
				return source.MakeError(def.Location,
					E_DuplicateVdTypeId {
						Id: id.String(),
					})
			}
			occurred[id] = struct{}{}
			// 2. check parameters
			if len(def.Parameters) > 0 {
				return source.MakeError(def.Location,
					E_GenericTypeDeclaredSerializable {})
			}
			// 3. check content
			var vd_type, ok = (func() (*vd.Type, bool) {
				switch content := def.Content.(type) {
				case *typsys.Box:
					var _, is_record = getRecord(content.InnerType)
					if is_record {
						return vd.AlgebraicType(vd.Record, id), true
					} else {
						return vd.AlgebraicType(vd.Tuple, id), true
					}
				case *typsys.Enum:
					return vd.AlgebraicType(vd.Enum, id), true
				default:
					return nil, false
				}
			})()
			if !(ok) {
				return source.MakeError(def.Location,
					E_GenericTypeDeclaredSerializable {
						TypeDefName: def.Name.String(),
					})
			}
			mapping[def.TypeDef] = VdTypeInfo {
				Id:   id,
				Type: vd_type,
			}
			return nil
		})()
		source.ErrorsJoin(&errs, err)
	}
	return errs
	})()
	if err != nil { return nil, err }
	return mapping, nil
}


